package DEEPERsource.DEEPERsource.source.preprocessing;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import deeper.Protein;

/**
 * Implementation of preprocessor, intended to simplify
 * parsing task.
 * @author tfayruzo
 *
 */
public class POSGenPreprocessor implements Preprocessor {
	private static Log _log = LogFactory.getLog(POSGenPreprocessor.class);

	public Map<String, String> preprocessSentence(String sentence, Set<Protein> interactors, List<Protein> proteins) {
		Map<String, String> result = new HashMap<String, String>();
		Collections.sort(proteins);
		int offset = 0;
		/*
		//arguable tweaking. Removes leading section names of form 'OBJECTIVE :'. Executes only if we don't have protein in the beginning, 
		//thus exclude errorneous removing of protein name written in capitals.
		if(pList.get(0).startOffset[0]>0){
			Pattern p = Pattern.compile("^[A_Z\\s\\d]+:\\s+");
			Matcher m = p.matcher(sentence);
			if(m.find()){
				_log.debug(m.group());
				sentence = sentence.replace(m.group(), "");
				result.put("sectionName", m.group());
				offset -= m.group().length(); 
			}
		}*/
		//substituting interacting protein names with unique ID's
		int c1 = 0, c2 = 0; //term index
		for(ListIterator<Protein> i = proteins.listIterator(); i.hasNext();){
			Protein prot = i.next();
			if(interactors.contains(prot)){
				String id = "ccoterm"+c1;
				int length = prot.endOffset[0] - prot.startOffset[0] + 1;
				prot.startOffset_mod = prot.startOffset[0] + offset;
				prot.endOffset_mod = prot.startOffset_mod + id.length()-1;
				prot.name_mod = id;
				sentence = sentence.substring(0,prot.startOffset_mod) + id + sentence.substring(prot.endOffset[prot.endOffset.length-1]+offset+1);
				offset += id.length() - length;
 				result.put(id, prot.name);
				c1++;
				Protein prot2 = null;
				if(i.hasPrevious() && prot.startOffset_mod>0 && sentence.charAt(prot.startOffset_mod-1)=='/'){
					i.previous();
					if(i.hasPrevious())
						prot2 = i.previous();
					else
						continue;
					i.next();
					i.next();
				}else if(i.hasNext() && sentence.length()>prot.endOffset_mod+1 && sentence.charAt(prot.endOffset_mod+1)=='/'){
					prot2 = i.next();
					i.previous();
				}
				/*if(prot2 != null){
					Map<String,Object> ret = shorteningDetection(sentence, prot, prot2, interactors, c2, id, result, offset);
					if(ret == null)
						continue;
					offset = (Integer)ret.get("offset");
					c2 = (Integer)ret.get("c2");
					sentence = (String)ret.get("sentence");
				}*/				
			}			
		}
		sentence = htmlReplacements(sentence, interactors);
		sentence = referenceReplacement(sentence, interactors);
		result.put("sentence", sentence);
		return result;
	}
	
	//not used now
	private Map<String, Object> shorteningDetection(String sentence, Protein prot, Protein prot2, Set<Protein> interactors, int c2, String id, Map<String,String> result, int offset){
		//performing following optimization: if 2 protein names are separated with '/' and one is one-word and another is
		//more than one word, then it is shortening
		// 0 - not occur, 1 - complex,  2 - not complex
		byte complex1 = 0, complexNext = 0, complexPrev = 0;;
		if(prot.name.contains(" "))
			complex1 = 1;
		else
			complex1 = 2;
		
		if((interactors.contains(prot2) && prot2.endOffset_mod == prot.startOffset_mod-2) ||
				(prot2.endOffset[0] == prot.startOffset[0]-2))
			if(prot2.name.contains(" "))
				complexPrev = 1;
			else
				complexPrev = 2;
		
		if((interactors.contains(prot2) && prot2.startOffset_mod == prot.endOffset_mod+2) ||
				(prot2.startOffset[0] == prot.endOffset[0]+2))
			if(prot2.name.contains(" "))
				complexNext = 1;
			else
				complexNext = 2;
		
		if(complex1==1 && complexNext==2){ // pComplex/_ => pComplex(ccoid) transformationType = 1
			if(interactors.contains(prot2))
				return null;
			String sid = "(ccoid"+c2+")";
			String shortening = prot2.name;
			sentence = sentence.replace(prot.name_mod+"/"+shortening, prot.name_mod+sid);
			offset += sid.length() - shortening.length() - 1;
			result.put("ccoid"+c2, prot2.name);
			result.put("transformationType", "1");
		}else if(complex1 == 1 && complexPrev == 2){ // _/pComplex => pComplex(ccoid) transformationType = 2a, 2b
			boolean interactingProtein = interactors.contains(prot2);
			if(interactingProtein){
				int startOffset = prot2.startOffset[0];
				prot.startOffset_mod = startOffset;
				prot.endOffset_mod = startOffset + id.length();
				prot2.startOffset_mod = prot.endOffset_mod + 2;
				prot2.endOffset_mod = prot2.startOffset_mod + prot2.name_mod.length();
				String sid = "("+prot2.name_mod+")";
				sentence = sentence.replace(prot2.name_mod+"/"+prot.name_mod, prot.name_mod+sid);
				offset += 1;
				result.put("transformationType", "2a");
			}else{
				int startOffset = prot2.startOffset[0];
				prot.startOffset_mod = startOffset;
				prot.endOffset_mod = startOffset + id.length();
				String sid = "(ccoid"+c2+")";
				offset += sid.length() - prot2.name.length() - 1;
				sentence = sentence.replace(prot2.name+"/"+prot.name_mod, prot.name_mod+sid);
				result.put("ccoid"+c2, prot2.name);
				c2++;
				result.put("transformationType", "2b");
			}					
		}else if(complex1 == 2 && complexNext == 1){// p/_ _ _ => ccoid(p) transformationType = 3
			if(interactors.contains(prot2))
				return null;
			String full = prot2.name;
			String sid = "ccoid"+c2;
			sentence = sentence.replace(prot.name_mod+"/"+full, sid+"("+prot.name_mod+")");
			offset += sid.length() - full.length() + 1;
			result.put("ccoid"+c2, prot2.name);
			c2++;
			result.put("transformationType", "3");
		}else if(complex1 == 2 && complexPrev == 1){ // _ _ _/p => ccoid(p) transformationType = 4a, 4b
			boolean interactingProtein = interactors.contains(prot2);
			String sid = "("+prot.name_mod+")";
			if(interactingProtein){
				sentence = sentence.replace(prot2.name_mod+"/"+prot.name_mod, prot2.name_mod+sid);
				offset += 1;
				result.put("transformationType", "4a");
			}else{
				String full = prot2.name;
				String shortening = "ccoid"+c2;
				sentence = sentence.replace(full+"/"+prot.name_mod, shortening+sid);
				result.put("ccoid"+c2, full);
				c2++;
				offset += shortening.length() - full.length() + 1;
				result.put("transformationType", "4b");
			}					
		}
		Map<String, Object> ret = new HashMap<String, Object>();
		ret.put("sentence", sentence);
		ret.put("c2", c2);
		ret.put("offset", offset);
		return ret;
	}
	
	private void readjustProteinPositions(int[] oldInterval, int[] newInterval, Collection<Protein> proteins){
		int offset = newInterval[1]-newInterval[0] - oldInterval[1] + oldInterval[0];
		for(Protein p : proteins){
			if(p.startOffset_mod>oldInterval[1]){
				p.startOffset_mod += offset;
				p.endOffset_mod += offset;
			}
		}
	}
	
	private String htmlReplacements(String sentence, Collection<Protein> interactors){
		Pattern p = Pattern.compile("&(quot)|(amp)|(gt)|(lt)");
		Matcher m = p.matcher(sentence);
		while(m.find()){
			String group = m.group();
			if(group.equals("&quot"))
				sentence = sentence.replace(group, "'");
			else if(group.equals("&amp"))
				sentence = sentence.replace(group, "&");
			else if(group.equals("&gt"))
				sentence = sentence.replace(group, ">");
			else if(group.equals("&lt"))
				sentence = sentence.replace(group, "<");
			readjustProteinPositions(new int[]{m.start(), m.end()}, new int[]{m.start(),m.start()+1},interactors);
			m = p.matcher(sentence);
		}
		return sentence;
	}
	
	private String findBoundary(int[] interval, String sentence){
		String begin = sentence.substring(0,interval[0]);
		String end = sentence.substring(interval[1]);
		int openBr1 = begin.lastIndexOf("(");
		int openBr2 = begin.lastIndexOf("[");
		char openBr, closeBr;
		if(openBr1<0 && openBr2<0){
			return sentence;
		}else if(openBr1>openBr2){
			openBr = '(';
			closeBr = ')';
		}else{
			openBr = '[';
			closeBr = ']';
		}
		CharacterIterator it = new StringCharacterIterator(begin);
		int counter = 0;
		int startIndex = 0;
		for(char c = it.last(); c != CharacterIterator.DONE; c = it.previous()){
			if(c == closeBr)
				counter--;
			if(c == openBr)
				counter++;			
			if(counter==1){
				startIndex = it.getIndex();
				break;
			}
		}
		it = new StringCharacterIterator(end);
		counter = 1;
		int endIndex = 0;
		for(char c = it.first(); c != CharacterIterator.DONE; c = it.next()){
			if(c == openBr)
				counter++;
			if(c == closeBr)
				counter--;
			if(counter==0){
				endIndex = it.getIndex();
				break;
			}
		}
		endIndex = begin.length() + interval[1] - interval[0] + 1 + endIndex;
		return sentence.substring(startIndex, endIndex);
	}
	
	/**
	 * Replaces references to the literature like [1] or (J.Jones, Boinformatics)
	 * @param sentence
	 * @param interactors
	 * @return
	 */
	public String referenceReplacement(String sentence, Collection<Protein> interactors){
		//first replace references like [1]
		Pattern p = Pattern.compile("\\[[\\d,\\-\\s]+\\]");
		Matcher m = p.matcher(sentence);
		while(m.find()){
			String group = m.group();
			sentence = sentence.replace(group, "");
			readjustProteinPositions(new int[]{m.start(), m.end()}, new int[]{m.start(),m.start()},interactors);
		}
		
		//secondly, looking for name patterns like J.Jones,and replace all in same brackets
		p = Pattern.compile("(([A-Z]\\.\\s*)+[A-Z][a-z'-]+)|([A-Z][a-z'-]+, ([A-Z]\\.\\s*))");
		m = p.matcher(sentence);
		while(m.find()){
			String surroundingGroup = findBoundary(new int[]{m.start(),m.end()}, sentence);
			int start = sentence.indexOf(surroundingGroup);
			int[] oldBound = new int[]{start, start + surroundingGroup.length()};
			int[] newBound = new int[]{start, start};			
			sentence = sentence.replace(surroundingGroup, "");
			readjustProteinPositions(oldBound, newBound, interactors);
			m = p.matcher(sentence);
		}		
		return sentence;		
	}
}
